﻿using System;
using System.Threading;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;

using GraphicalObjects;
using DataObjects;
using DataObjects.Enums;
using GameRules;

namespace SimpleAI3D
{
    public partial class Form1 : Form
    {
        private Device device = null;

        public Form1()
        {
            InitializeComponent();

            // Set Window Style
            this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.Opaque, true);
        }

        public void InitializeGraphics()
        {
            try
            {
                PresentParameters pPrams = new PresentParameters();
                pPrams.Windowed = true;
                pPrams.SwapEffect = SwapEffect.Discard;
                pPrams.EnableAutoDepthStencil = true;
                pPrams.AutoDepthStencilFormat = DepthFormat.D16;
                device = new Device(0, DeviceType.Hardware, this, CreateFlags.SoftwareVertexProcessing, pPrams);

                device.DeviceReset += new EventHandler(this.OnResetDevice);
                OnResetDevice(device, null);
            }
            catch (DirectXException e)
            {
                throw e;
            }
        }

        private delegate void InvokeAddSceneObject(GameObject obj);
        public void AddSceneObject(GameObject obj)
        {
            if (InvokeRequired && !IsDisposed)
            {
                Invoke(new InvokeAddSceneObject(AddSceneObject), obj);
                return;
            }

            if (device == null)
                return;

            GameFactory.BuildGraphicalObject(obj, ref device);
        }

        private delegate void InvokeUpdateView();
        public void UpdateView()
        {
            if (InvokeRequired && !IsDisposed)
            {
                Invoke(new InvokeUpdateView(UpdateView));
                return;
            }

            Render();
        }

        private void OnResetDevice(object sender, EventArgs e)
        {
            Device device = (Device)sender;

            device.RenderState.ZBufferEnable = true;
            device.RenderState.Ambient = Color.White;
            device.RenderState.Lighting = true;

            //foreach (GraphicalBase gameObj in GameScene.SceneObjects)
            //    gameObj.ResetMesh(ref device);
        }

        private void SetupLights()
        {
            Color col = Color.White;

            Material material = new Material();
            material.Diffuse = col;
            material.Ambient = col;

            device.Material = material;

            device.Lights[0].Type = LightType.Directional;
            device.Lights[0].Diffuse = Color.DarkTurquoise;
            device.Lights[0].Enabled = true;

            device.RenderState.Ambient = Color.White;
        }

        private void SetupMatrices()
        {
            // For our world matrix, we will just leave it as the identity
            //device.Transform.World = Matrix.RotationAxis(new Vector3((float)Math.Cos(Environment.TickCount / 1000f), 1, (float)Math.Sin(Environment.TickCount / 500f)), Environment.TickCount / 3000f);
            //device.Transform.World = Matrix.RotationY(Environment.TickCount / 1000.0f);

            // Set up our view matrix. A view matrix can be defined given an eye point, a point to lookat, and a
            // direction for which way is up. Here, we set the eye five units back along the z-axis and up three
            // units, look at the origin, and define "up" to be in the y-direction.
            device.Transform.View =
                Matrix.LookAtLH(new Vector3(50.1f, 125.0f, 50.0f),
                                new Vector3(50.0f, 0.0f, 50.0f),
                                new Vector3(0.0f, 1.0f, 0.0f))
              //* Matrix.RotationY(Environment.TickCount / 4000.0f)
              ;

            // For the projection matrix, we set up a perspective transform (which transforms geometry from 3D
            // view space to 2D viewport space, with a perspective divide making objects smaller in the distance).
            // To build a perpsective transform, we need the field of view (1/4 pi is common), the aspect ratio,
            // and the near and far clipping planes (which define at what distances geometry should be no longer
            // be rendered).
            device.Transform.Projection = Matrix.PerspectiveFovLH((float)(Math.PI / 4), 1.0f, 0.1f, 1000.0f);
        }

        private void Render()
        {
            if (device == null)
                return;

            device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.Black, 1.0f, 0);
            device.BeginScene();

            SetupLights();
            SetupMatrices();

            foreach (GraphicalBase gameObj in GameScene.SceneObjects)
            {
                //gameObj.XRotation = Environment.TickCount / 500.0f;
                gameObj.YRotation = Environment.TickCount / 1000.0f;
                //gameObj.ZRotation = Environment.TickCount / 1500.0f;
                gameObj.Draw(ref device);
            }

            device.EndScene();
            device.Present();
        }
    }


    //---------------------------------------------------------------------------------------------------------

    class NewGameThread
    {
        public Form1 theForm = null;
        public Thread t;
        private int sleep = 2000; // default of 2 second wait time.

        public NewGameThread(string text)
        {
            t = new Thread(new ThreadStart(this.run));
        }

        public NewGameThread(string text, int sleep)
        {
            t = new Thread(new ThreadStart(this.run));
            this.sleep = sleep;
        }

        public void Start()
        {
            if (t == null || theForm == null)
                return;
            t.Start();
        }

        void run()
        {
            GameFactory.InitialiseSystems();
            
            GameObject obj = GameFactory.BuildGamePiece(BahaviourTypes.Aggresive, 1);
            GameMap.Add(obj);
            theForm.AddSceneObject(obj);
            GameScene.SceneObjects[GameScene.SceneObjects.Count - 1].Position = new Vector3(obj.MapX, 0, obj.MapY);
            GameScene.SceneObjects[GameScene.SceneObjects.Count - 1].ScaleAbs(0.1f, 0.1f, 0.1f);

            obj = GameFactory.BuildGamePiece(BahaviourTypes.Defensive, 1);
            GameMap.Add(obj);
            theForm.AddSceneObject(obj);
            GameScene.SceneObjects[GameScene.SceneObjects.Count - 1].Position = new Vector3(obj.MapX, 0, obj.MapY);
            GameScene.SceneObjects[GameScene.SceneObjects.Count - 1].ScaleAbs(0.1f, 0.1f, 0.1f);

            obj = GameFactory.BuildGamePiece(BahaviourTypes.Normal, 2);
            GameMap.Add(obj);
            theForm.AddSceneObject(obj);
            GameScene.SceneObjects[GameScene.SceneObjects.Count - 1].Position = new Vector3(obj.MapX, 0, obj.MapY);
            GameScene.SceneObjects[GameScene.SceneObjects.Count - 1].ScaleAbs(0.1f, 0.1f, 0.1f);
            
            obj = GameFactory.BuildGamePiece(BahaviourTypes.Defensive, 2);
            GameMap.Add(obj);
            theForm.AddSceneObject(obj);
            GameScene.SceneObjects[GameScene.SceneObjects.Count - 1].Position = new Vector3(obj.MapX, 0, obj.MapY);
            GameScene.SceneObjects[GameScene.SceneObjects.Count - 1].ScaleAbs(0.1f, 0.1f, 0.1f);

            obj = GameFactory.BuildGamePiece(BahaviourTypes.Aggresive, 3);
            GameMap.Add(obj);
            theForm.AddSceneObject(obj);
            GameScene.SceneObjects[GameScene.SceneObjects.Count - 1].Position = new Vector3(obj.MapX, 0, obj.MapY);
            GameScene.SceneObjects[GameScene.SceneObjects.Count - 1].ScaleAbs(0.1f, 0.1f, 0.1f);
            
            obj = GameFactory.BuildGamePiece(BahaviourTypes.Normal, 3);
            GameMap.Add(obj);
            theForm.AddSceneObject(obj);
            GameScene.SceneObjects[GameScene.SceneObjects.Count - 1].Position = new Vector3(obj.MapX, 0, obj.MapY);
            GameScene.SceneObjects[GameScene.SceneObjects.Count - 1].ScaleAbs(0.1f, 0.1f, 0.1f);

            obj = GameFactory.BuildGamePiece(BahaviourTypes.Normal, 4);
            GameMap.Add(obj);
            theForm.AddSceneObject(obj);
            GameScene.SceneObjects[GameScene.SceneObjects.Count - 1].Position = new Vector3(obj.MapX, 0, obj.MapY);
            GameScene.SceneObjects[GameScene.SceneObjects.Count - 1].ScaleAbs(0.1f, 0.1f, 0.1f);
            
            obj = GameFactory.BuildGamePiece(BahaviourTypes.Aggresive, 4);
            GameMap.Add(obj);
            theForm.AddSceneObject(obj);
            GameScene.SceneObjects[GameScene.SceneObjects.Count - 1].Position = new Vector3(obj.MapX, 0, obj.MapY);
            GameScene.SceneObjects[GameScene.SceneObjects.Count - 1].ScaleAbs(0.1f, 0.1f, 0.1f);

            obj = GameFactory.BuildStarbase(BahaviourTypes.Defensive, 5);
            GameMap.Add(obj);
            theForm.AddSceneObject(obj);
            GameScene.SceneObjects[GameScene.SceneObjects.Count - 1].Position = new Vector3(obj.MapX, 0, obj.MapY);
            GameScene.SceneObjects[GameScene.SceneObjects.Count - 1].ScaleAbs(0.1f, 0.1f, 0.1f);
            
            obj = GameFactory.BuildStarbase(BahaviourTypes.Defensive, 5);
            GameMap.Add(obj);
            theForm.AddSceneObject(obj);
            GameScene.SceneObjects[GameScene.SceneObjects.Count - 1].Position = new Vector3(obj.MapX, 0, obj.MapY);
            GameScene.SceneObjects[GameScene.SceneObjects.Count - 1].ScaleAbs(0.1f, 0.1f, 0.1f);

            //Thread.Sleep(sleep);
            GlobalData.ResetTime(Environment.TickCount);

            int teams = GameMap.TeamsRemaining();
            while (GameMap.TeamsRemaining() > 1)
            {
                if (theForm.IsDisposed)
                    break;

                GlobalData.MarkTime(Environment.TickCount);

                List<GameObject> activePieces = GameMap.GetPieces(true);

                foreach (GameObject gameObj in activePieces)
                    gameObj.Behaviour.PreTurnProcessing();

                theForm.UpdateView();

                foreach (GameObject gameObj in activePieces)
                    gameObj.Behaviour.ProcessTurn();
                foreach (GameObject gameObj in activePieces)
                    gameObj.Behaviour.PostTurnProcessing();
            }

            while(true)
                theForm.UpdateView();
        }
    }
}
